﻿using System;
using System.Diagnostics.Contracts;
using System.Threading.Tasks;

namespace DaveSexton.Labs
{
	/// <summary>
	/// Represents an experiment implemented by a delegate.
	/// </summary>
	public sealed class AnonymousExperiment : IExperiment
	{
		#region Public Properties
		/// <inheritdoc />
		public string DisplayName
		{
			get
			{
				return displayName;
			}
		}

		/// <inheritdoc />
		public string Description
		{
			get
			{
				return description;
			}
		}
		#endregion

		#region Private / Protected
		private readonly string displayName, description;
		private readonly Action execute;
		private readonly Func<Task> executeAsync;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="AnonymousExperiment" /> class.
		/// </summary>
		public AnonymousExperiment(Action execute)
			: this(MethodExperiment.DefaultExperimentName, string.Empty, execute)
		{
			Contract.Requires(execute != null);
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="AnonymousExperiment" /> class.
		/// </summary>
		public AnonymousExperiment(Func<Task> executeAsync)
			: this(MethodExperiment.DefaultExperimentName, string.Empty, executeAsync)
		{
			Contract.Requires(executeAsync != null);
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="AnonymousExperiment" /> class.
		/// </summary>
		public AnonymousExperiment(string displayName, Action execute)
			: this(displayName, string.Empty, execute)
		{
#if !WINDOWS_PHONE_70
			Contract.Requires(!string.IsNullOrWhiteSpace(displayName));
#else
			Contract.Requires(!string.IsNullOrEmpty(displayName));
#endif
			Contract.Requires(execute != null);
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="AnonymousExperiment" /> class.
		/// </summary>
		public AnonymousExperiment(string displayName, Func<Task> executeAsync)
			: this(displayName, string.Empty, executeAsync)
		{
#if !WINDOWS_PHONE_70
			Contract.Requires(!string.IsNullOrWhiteSpace(displayName));
#else
			Contract.Requires(!string.IsNullOrEmpty(displayName));
#endif
			Contract.Requires(executeAsync != null);
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="AnonymousExperiment" /> class.
		/// </summary>
		public AnonymousExperiment(string displayName, string description, Action execute)
		{
#if !WINDOWS_PHONE_70
			Contract.Requires(!string.IsNullOrWhiteSpace(displayName));
#else
			Contract.Requires(!string.IsNullOrEmpty(displayName));
#endif
			Contract.Requires(description != null);
			Contract.Requires(execute != null);

			this.displayName = displayName;
			this.description = description;
			this.execute = execute;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="AnonymousExperiment" /> class.
		/// </summary>
		public AnonymousExperiment(string displayName, string description, Func<Task> executeAsync)
		{
#if !WINDOWS_PHONE_70
			Contract.Requires(!string.IsNullOrWhiteSpace(displayName));
#else
			Contract.Requires(!string.IsNullOrEmpty(displayName));
#endif
			Contract.Requires(description != null);
			Contract.Requires(executeAsync != null);

			this.displayName = displayName;
			this.description = description;
			this.executeAsync = executeAsync;
		}
		#endregion

		#region Methods
		[ContractInvariantMethod]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Required for code contracts.")]
		private void ObjectInvariant()
		{
#if !WINDOWS_PHONE_70
			Contract.Invariant(!string.IsNullOrWhiteSpace(displayName));
#else
			Contract.Invariant(!string.IsNullOrEmpty(displayName));
#endif
			Contract.Invariant(description != null);
			Contract.Invariant((execute == null) != (executeAsync == null));
		}

		/// <inheritdoc />
		public Task ExecuteAsync()
		{
			if (execute != null)
			{
				execute();

#if (SILVERLIGHT && !WINDOWS_PHONE) || NET40
				return TaskEx.FromResult(true);
#else
				return Task.FromResult(true);
#endif
			}
			else
			{
				return executeAsync();
			}
		}
		#endregion
	}
}